/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2003-2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;

using Gtk;
using Glade;

using Anculus.Core;

using Galaxium.Client;
using Galaxium.Core;
using Galaxium.Gui;
using Galaxium.Gui.GtkGui;
using Galaxium.Protocol;
using Galaxium.Protocol.Gui;

using Anculus.Gui;


namespace Galaxium.Client.GtkGui
{
	public sealed class TransferWindow
	{
		[Widget ("TransferWindow")]
		private Window _window;
		[Widget ("treeTransfers")]
		private TreeView _treeTransfers;
		[Widget ("labelAccount")]
		private Label _labelAccount;
		[Widget ("labelContact")]
		private Label _labelContact;
		[Widget ("labelDestination")]
		private Label _labelDestination;
		[Widget ("buttonCancel")]
		private Button _buttonCancel;
		[Widget ("buttonOpen")]
		private Button _buttonOpen;
		[Widget ("buttonClear")]
		private Button _buttonClear;
		[Widget ("checkClose")]
		private CheckButton _checkClose;
		[Widget ("checkClear")]
		private CheckButton _checkClear;
		[Widget ("statusbar")]
		private Statusbar _statusbar;
		
		private int _windowX = 0, _windowY = 0, _windowH = 0, _windowW = 0;
		private ListStore _treeStore;
		private Dictionary<IFileTransfer, TreeIter> _treeLookup = new Dictionary<IFileTransfer, TreeIter> ();
		private IConfigurationSection _config;
		
		private System.Timers.Timer _updateTimer = new System.Timers.Timer(250);
		
		public bool Visible
		{
			get { return(_window.Visible); }
			set
			{
				if (value == _window.Visible)
					return;
				
				if (!value)
				{
					lock (_updateTimer)
						_updateTimer.Stop ();
					
					_window.GetPosition(out _windowX, out _windowY);
					_window.GetSize(out _windowW, out _windowH);
				}
				else
				{
					lock (_updateTimer)
						_updateTimer.Start ();
					
					_window.Move(_windowX, _windowY);
					_window.Resize(_windowW, _windowH);
				}
				
				_window.Visible = value;
			}
		}
		
		public TransferWindow ()
		{
			XML gxml = new XML (GladeUtility.GetGladeResourceStream (typeof (TransferWindow).Assembly, "TransferWindow.glade"), null, null);
			gxml.Autoconnect (this);
			
			_config = Configuration.Transfer.Section;
			
			_window.Icon = IconUtility.GetIcon ("galaxium-transfer", IconSizes.Small);
			
			_windowX = _config.GetInt("X", 100);
			_windowY = _config.GetInt("Y", 100);
			_windowW = _config.GetInt("Width", 500);
			_windowH = _config.GetInt("Height", 400);
			
			_updateTimer.Elapsed += delegate
			{
				ThreadUtility.SyncDispatch (new VoidDelegate (delegate
				{
					UpdateTransferTree ();
				}));
			};
			
			_buttonCancel.Sensitive = false;
			_buttonOpen.Sensitive = false;
			
			_treeStore = new ListStore(typeof(IFileTransfer));
			
			_treeTransfers.Model = _treeStore;
			_treeTransfers.HeadersClickable = true;
			_treeTransfers.RulesHint = true;
			_treeTransfers.Selection.Changed += SelectionChanged;
			
			TreeViewColumn filenameColumn = new TreeViewColumn ();
			filenameColumn.Title = "Filename";
			CellRendererPixbuf imageCell = new CellRendererPixbuf ();
			CellRendererText filenameCell = new CellRendererText ();
			filenameColumn.PackStart (imageCell, false);
			filenameColumn.PackEnd (filenameCell, true);
 			filenameColumn.Expand = true;
 			
			TreeViewColumn transferedColumn = new TreeViewColumn ();
			transferedColumn.Title = "Transfered";
			CellRendererText transferedCell = new CellRendererText ();
			transferedColumn.PackStart (transferedCell, true);
			transferedColumn.MinWidth = 75;
			transferedColumn.Alignment = 1.0f;
			
			TreeViewColumn filesizeColumn = new TreeViewColumn ();
			filesizeColumn.Title = "Total";
			CellRendererText filesizeCell = new CellRendererText ();
			filesizeColumn.PackStart (filesizeCell, true);
			filesizeColumn.MinWidth = 75;
			filesizeColumn.Alignment = 1.0f;
			
			TreeViewColumn speedColumn = new TreeViewColumn ();
			speedColumn.Title = "Speed";
			CellRendererText speedCell = new CellRendererText ();
			speedColumn.PackStart (speedCell, true);
			speedColumn.MinWidth = 75;
			speedColumn.Alignment = 1.0f;
			
			TreeViewColumn statusColumn = new TreeViewColumn ();
			statusColumn.Title = "ETA";
			CellRendererText statusCell = new CellRendererText ();
			statusColumn.PackStart (statusCell, true);
			statusColumn.MinWidth = 90;
			statusColumn.Alignment = 1.0f;
			
			TreeViewColumn percentColumn = new TreeViewColumn ();
			percentColumn.Title = "%";
			CellRendererProgress percentCell = new CellRendererProgress ();
			percentColumn.PackStart (percentCell, true);
			percentColumn.MinWidth = 60;
			percentColumn.Alignment = 1.0f;
			
			filenameColumn.SetCellDataFunc (imageCell, new TreeCellDataFunc (RenderImage));
 			filenameColumn.SetCellDataFunc (filenameCell, new TreeCellDataFunc (RenderFilename));
 			//transferedColumn.SetCellDataFunc (transferedCell, new TreeCellDataFunc (RenderTransfered));
			//filesizeColumn.SetCellDataFunc (filesizeCell, new TreeCellDataFunc (RenderFilesize));
			//speedColumn.SetCellDataFunc (speedCell, new TreeCellDataFunc (RenderSpeed));
			//statusColumn.SetCellDataFunc (statusCell, new TreeCellDataFunc (RenderStatus));
			percentColumn.SetCellDataFunc (percentCell, new TreeCellDataFunc (RenderProgress));
			
			_treeTransfers.AppendColumn (filenameColumn);
			//_treeTransfers.AppendColumn (transferedColumn);
			//_treeTransfers.AppendColumn (filesizeColumn);
			//_treeTransfers.AppendColumn (speedColumn);
			_treeTransfers.AppendColumn (percentColumn);
			//_treeTransfers.AppendColumn (statusColumn);
			
			_treeTransfers.HeadersVisible = false;
			_treeTransfers.RulesHint = true;
			
			_treeTransfers.RowActivated += TransferActivated;
			
 			filenameColumn.Sizing = TreeViewColumnSizing.Autosize;
			filesizeColumn.Sizing = TreeViewColumnSizing.Autosize;
			speedColumn.Sizing = TreeViewColumnSizing.Autosize;
			statusColumn.Sizing = TreeViewColumnSizing.Autosize;
			percentColumn.Sizing = TreeViewColumnSizing.Autosize;
			
			FileTransferUtility.TransferAdded += TransferAdded;
			FileTransferUtility.TransferRemoved += TransferRemoved;
			
			UpdateTransferCount ();
			
			_checkClear.Active = _config.GetBool (Configuration.Transfer.AutoClear.Name, Configuration.Transfer.AutoClear.Default);
			_checkClose.Active = _config.GetBool (Configuration.Transfer.AutoClose.Name, Configuration.Transfer.AutoClose.Default);
			
			_treeTransfers.ShowAll();
			_window.Visible = false;
		}
		
		public void SaveWindowLocation ()
		{
			_config.SetInt ("X", _windowX);
			_config.SetInt ("Y", _windowY);
			_config.SetInt ("Width", _windowW);
			_config.SetInt ("Height", _windowH);
		}
		
		private void TransferActivated (object sender, RowActivatedArgs args)
		{
			TreeIter iter;
			
			_treeStore.GetIter(out iter, args.Path);
			
			IFileTransfer transfer = (IFileTransfer)_treeStore.GetValue(iter, 0);
			
			if (transfer != null)
			{
				if (transfer.Incoming)
					Process.Start ("file://"+Path.Combine(transfer.Destination, transfer.FileName));
				else
					Process.Start ("file://"+transfer.FileName);
			}
		}
		
		private void ButtonCancelClicked (object sender, EventArgs args)
		{
			if (_treeTransfers.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeTransfers.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return;
				
				IFileTransfer transfer = (IFileTransfer)_treeStore.GetValue(iter, 0);
				
				if (transfer != null)
				{
					transfer.Abort ();
				}
			}
		}
		
		private void ButtonOpenClicked (object sender, EventArgs args)
		{
			if (_treeTransfers.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeTransfers.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return;
				
				IFileTransfer transfer = (IFileTransfer)_treeStore.GetValue(iter, 0);
				
				if (transfer != null)
				{
					if (transfer.Incoming)
						Process.Start ("file://" + transfer.Destination);
					else
						Process.Start ("file://" + Path.GetDirectoryName(transfer.FileName));
				}
			}
		}
		
		private void ButtonClearClicked (object sender, EventArgs args)
		{
			FileTransferUtility.RemoveInactive ();
		}
		
		private void CheckCloseToggled (object sender, EventArgs args)
		{
			_config.SetBool (Configuration.Transfer.AutoClose.Name, _checkClose.Active); 
		}
		
		private void CheckClearToggled (object sender, EventArgs args)
		{
			_config.SetBool (Configuration.Transfer.AutoClear.Name, _checkClear.Active);
		}
		
		private void SelectionChanged (object sender, EventArgs args)
		{
			UpdateSelectedTransfer ();
		}
		
		private void UpdateSelectedTransfer ()
		{
			if (FileTransferUtility.Transfers.Count < 1)
			{
				_labelAccount.Text = "";
				_labelContact.Text = "";
				_labelDestination.Text = "";
				
				_buttonCancel.Sensitive = false;
				_buttonOpen.Sensitive = false;
				return;
			}
			
			if (_treeTransfers.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeTransfers.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return;
				
				IFileTransfer transfer = (IFileTransfer)_treeStore.GetValue(iter, 0);
				
				if (transfer != null)
				{
					_labelAccount.Text = transfer.Session.Account.UniqueIdentifier;
					_labelContact.Text = transfer.Contact.UniqueIdentifier;
					_labelDestination.Text = transfer.Destination;
					
					if (transfer.State == TransferState.Started || transfer.State == TransferState.Progressing || transfer.State == TransferState.Pending)
						_buttonCancel.Sensitive = true;
					else
						_buttonCancel.Sensitive = false;
					
					if (!transfer.Incoming)
						_buttonOpen.Sensitive = true;
					else if (transfer.Incoming && transfer.State == TransferState.Finished)
						_buttonOpen.Sensitive = true;
					else
						_buttonOpen.Sensitive = false;
				}
			}
		}
		
		private void TransferWindowDelete (object sender, DeleteEventArgs args)
		{
			args.RetVal = true;
			
			GalaxiumUtility.TransferWindow.Visible = !GalaxiumUtility.TransferWindow.Visible;
		}
		
		private void UpdateTransferCount ()
		{
			string result = "";
			
			if (FileTransferUtility.ActiveCount == 0)
				result += "No active transfers";
			else
				result += FileTransferUtility.ActiveCount + " active transfer(s)";
			
			result += ", ";
			
			if (FileTransferUtility.InactiveCount == 0)
				result += "No inactive transfers";
			else
				result += FileTransferUtility.InactiveCount + " inactive transfer(s)";
			
			_statusbar.Push (0, result);
		}
		
		private void TransferAdded (TransferEventArgs args)
		{
			UpdateTransferCount ();
			
			args.Transfer.TransferCancel += TransferCancelled;
			args.Transfer.TransferDecline += TransferDeclined;
			args.Transfer.TransferFinish += TransferFinished;
			args.Transfer.TransferLocalAbort += TransferLocallyAborted;
			args.Transfer.TransferProgress += TransferProgressed;
			args.Transfer.TransferRemoteAbort += TransferRemotelyAborted;
			args.Transfer.TransferStart += TransferStarted;
			args.Transfer.TransferFailed += TransferFailed;
			
			AddTransferToTree (args.Transfer);
		}
		
		private void TransferRemoved (TransferEventArgs args)
		{
			UpdateTransferCount ();
			
			args.Transfer.TransferCancel -= TransferCancelled;
			args.Transfer.TransferDecline -= TransferDeclined;
			args.Transfer.TransferFinish -= TransferFinished;
			args.Transfer.TransferLocalAbort -= TransferLocallyAborted;
			args.Transfer.TransferProgress -= TransferProgressed;
			args.Transfer.TransferRemoteAbort -= TransferRemotelyAborted;
			args.Transfer.TransferStart -= TransferStarted;
			args.Transfer.TransferFailed -= TransferFailed;
			
			RemoveTransferFromTree (args.Transfer);
		}
		
		public void Hide ()
		{
			Visible = false;
		}
		
		private void SelectFirstTransfer ()
		{
			TreeIter iter;
			
			if(_treeStore.GetIterFirst(out iter))
			{
				TreePath path = _treeStore.GetPath(iter);
				_treeTransfers.SetCursor(path, null, false);
			}
		}
		
		public void Show ()
		{
			SelectFirstTransfer ();
			
			Visible = true;
		}
		
		public void Destroy ()
		{
			_window.Hide();
			_window.Destroy();
		}
		
		private void RenderImage (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			
			if (transfer.Incoming)
				(cell as CellRendererPixbuf).Pixbuf = IconUtility.GetIcon ("galaxium-transfer-receive", IconSizes.Large);
			else
				(cell as CellRendererPixbuf).Pixbuf = IconUtility.GetIcon ("galaxium-transfer-send", IconSizes.Large);
		}
		
		private void RenderFilename (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			string text = string.Empty;
			string state = string.Empty;
			string rate = string.Empty;
			
			switch (transfer.State)
			{
				case TransferState.Progressing:
					state = transfer.TimeLeft () + " left until completion.";
					rate = " at " + transfer.TransferRate () + "\n";
					break;
				
				default:
					state = transfer.State.ToString ();
					rate = "\n";
					break;
			}
			
			text += "<span size=\"small\"><b>Filename:</b> " + Path.GetFileName(transfer.FileName) + "\n";
			text += "<b>Transfered:</b> "+ BaseUtility.SizeString (transfer.TransferedBytes) + " of " + BaseUtility.SizeString (transfer.TotalBytes) + rate;
			text += "<b>State:</b> " + state + "</span>";
			
			cell.Xalign = 0.0f;
			(cell as CellRendererText).Ellipsize = Pango.EllipsizeMode.End;
			(cell as CellRendererText).Markup = text;
			
		}
		
		private void RenderTransfered (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			(cell as CellRendererText).Text = BaseUtility.SizeString (transfer.TransferedBytes);
			cell.Xalign = 1.0f;
		}
		
		private void RenderFilesize (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			(cell as CellRendererText).Text = BaseUtility.SizeString (transfer.TotalBytes);
			cell.Xalign = 1.0f;
		}
		
		private void RenderSpeed (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			
			
			
		}
		
		private void RenderStatus (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			
			cell.Xalign = 1.0f;
		}
		
		private void RenderProgress (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IFileTransfer transfer = (IFileTransfer) model.GetValue (iter, 0);
			
			switch (transfer.State)
			{
				case TransferState.Progressing:
					double divided = (double)transfer.TransferedBytes / (double)transfer.TotalBytes;
					int percent = (int)(divided*100);
					(cell as CellRendererProgress).Value = percent;
					(cell as CellRendererProgress).Text = percent+"%";
					(cell as CellRendererProgress).Visible = true;
					break;
				
				default:
					(cell as CellRendererProgress).Value = 0;
					(cell as CellRendererProgress).Text = "";
					(cell as CellRendererProgress).Visible = false;
					break;
			}
			
			cell.Xalign = 1.0f;
		}
		
		private void AddTransferToTree (IFileTransfer transfer)
		{
			TreeIter iter = _treeStore.AppendValues (transfer);
			_treeLookup.Add (transfer, iter);
		}
		
		private void RemoveTransferFromTree (IFileTransfer transfer)
		{
			bool selectedRemoved = false;
			
			TreeIter selectedIter = TreeIter.Zero;
			TreeIter iter = _treeLookup[transfer];
			_treeTransfers.Selection.GetSelected (out selectedIter);
			
			if (iter.Stamp == selectedIter.Stamp)
				selectedRemoved = true;
			
			_treeStore.Remove(ref iter);
			
			if (selectedRemoved)
				SelectFirstTransfer ();
		}
		
		private void TransferCancelled (object sender, FileTransferEventArgs args)
		{
			UpdateTransferTree ();
		}
		
		private void TransferDeclined (object sender, FileTransferEventArgs args)
		{
			UpdateTransferTree ();
		}
		
		private void TransferFinished (object sender, FileTransferEventArgs args)
		{
			_treeTransfers.QueueDraw ();
			UpdateSelectedTransfer ();
			UpdateTransferCount ();
			
			if (_checkClear.Active)
				FileTransferUtility.ClearCompleted ();
			
			if (_checkClose.Active && !FileTransferUtility.IsActive)
				Hide();
		}
		
		private void TransferLocallyAborted (object sender, FileTransferEventArgs args)
		{
			UpdateTransferTree ();
		}
		
		private void TransferProgressed (object sender, FileTransferEventArgs args)
		{
			Log.Debug ("Received progress event, updating timer status.");
			
			lock (_updateTimer)
			{
				if (!_updateTimer.Enabled)
					_updateTimer.Start();
			}
		}
		
		private void TransferRemotelyAborted (object sender, FileTransferEventArgs args)
		{
			UpdateTransferTree ();
		}
		
		private void TransferStarted (object sender, FileTransferEventArgs args)
		{
			UpdateTransferTree ();
		}
		
		private void TransferFailed (object sender, FileTransferErrorEventArgs args)
		{
			UpdateTransferTree ();
		}
		
		private void UpdateTransferTree ()
		{
			_treeTransfers.QueueDraw ();
			UpdateSelectedTransfer ();
			UpdateTransferCount ();
		}
	}
}